home *** CD-ROM | disk | FTP | other *** search
- Path: xanth!cs.odu.edu!Amiga-Request
- From: Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator)
- Newsgroups: comp.sources.amiga
- Subject: v90i024: cpp - a c preprocessor with some ANSI features, Part03/05
- Message-ID: <11032@xanth.cs.odu.edu>
- Date: 18 Jan 90 00:19:37 GMT
- Sender: tadguy@cs.odu.edu
- Reply-To: Olaf 'Rhialto' Seibert <U211344@HNYKUN11.BITNET>
- Lines: 1666
- Approved: tadguy@cs.odu.edu (Tad Guy)
-
- Submitted-by: Olaf 'Rhialto' Seibert <U211344@HNYKUN11.BITNET>
- Posting-number: Volume 90, Issue 024
- Archive-name: unix/cpp/part03
-
- #!/bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 3 (of 5)."
- # Contents: Cpp4.c cpp5.c
- # Wrapped by tadguy@xanth on Wed Jan 17 19:17:35 1990
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'Cpp4.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'Cpp4.c'\"
- else
- echo shar: Extracting \"'Cpp4.c'\" \(19642 characters\)
- sed "s/^X//" >'Cpp4.c' <<'END_OF_FILE'
- X/*
- X * C P P 4 . C
- X * M a c r o D e f i n i t i o n s
- X *
- X * Edit History
- X * 31-Aug-84 MM USENET net.sources release
- X * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring()
- X * so they work correctly with token concatenation.
- X * Added string formal recognition.
- X * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we
- X * don't print unnecessary error messages for
- X * #if !defined(FOO) && FOO != 0 && 10 / FOO ...
- X * 31-Oct-84 ado/MM Added token concatenation
- X * 6-Nov-84 MM Split off eval stuff
- X * 21-Oct-85 RMS Rename `token' to `tokenbuf'.
- X * In doundef, don't complain if arg already not defined.
- X * 14-Mar-86 FNF Incorporate macro based C debugging package.
- X * Port to Commodore AMIGA.
- X * 21-Aug-88 Ois Changed concatenation operator to ##. Changed hand-
- X * ling of tokens following ##. Added new meaning of #.
- X */
- X
- X#include <stdio.h>
- X#include <ctype.h>
- X#include "cppdef.h"
- X#include "cpp.h"
- X/*
- X * parm[], parmp, and parlist[] are used to store #define() argument
- X * lists. nargs contains the actual number of parameters stored.
- X */
- Xstatic char parm[NPARMWORK + 1]; /* define param work buffer */
- Xstatic char *parmp; /* Free space in parm */
- Xstatic char *parlist[LASTPARM]; /* -> start of each parameter */
- Xstatic int nargs; /* Parameters for this macro */
- X
- Xdodefine()
- X/*
- X * Called from control when a #define is scanned. This module
- X * parses formal parameters and the replacement string. When
- X * the formal parameter name is encountered in the replacement
- X * string, it is replaced by a character in the range 128 to
- X * 128+NPARAM (this allows up to 32 parameters within the
- X * Dec Multinational range). If cpp is ported to an EBCDIC
- X * machine, you will have to make other arrangements.
- X *
- X * There is some special case code to distinguish
- X * #define foo bar
- X * from #define foo() bar
- X *
- X * Also, we make sure that
- X * #define foo foo
- X * expands to "foo" but doesn't put cpp into an infinite loop.
- X *
- X * A warning message is printed if you redefine a symbol to a
- X * different text. I.e,
- X * #define foo 123
- X * #define foo 123
- X * is ok, but
- X * #define foo 123
- X * #define foo +123
- X * is not.
- X *
- X * The following subroutines are called from define():
- X * checkparm called when a token is scanned. It checks through the
- X * array of formal parameters. If a match is found, the
- X * token is replaced by a control byte which will be used
- X * to locate the parameter when the macro is expanded.
- X * textput puts a string in the macro work area (parm[]), updating
- X * parmp to point to the first free byte in parm[].
- X * textput() tests for work buffer overflow.
- X * charput puts a single character in the macro work area (parm[])
- X * in a manner analogous to textput().
- X */
- X{
- X register int c;
- X register DEFBUF *dp; /* -> new definition */
- X int isredefine; /* TRUE if redefined */
- X char *old; /* Remember redefined */
- X#if OK_CONCAT
- X register int quoting; /* Remember we saw a # */
- X#endif
- X extern int save(); /* Save char in work[] */
- X
- X DBUG_ENTER ("dodefine");
- X if (type[(c = skipws())] != LET)
- X goto bad_define;
- X isredefine = FALSE; /* Set if redefining */
- X if ((dp = lookid(c)) == NULL) /* If not known now */
- X dp = defendel(tokenbuf, FALSE); /* Save the name */
- X else { /* It's known: */
- X isredefine = TRUE; /* Remember this fact */
- X old = dp->repl; /* Remember replacement */
- X dp->repl = NULL; /* No replacement now */
- X }
- X parlist[0] = parmp = parm; /* Setup parm buffer */
- X if ((c = get()) == '(') { /* With arguments? */
- X nargs = 0; /* Init formals counter */
- X do { /* Collect formal parms */
- X if (nargs >= LASTPARM)
- X cfatal("Too many arguments for macro", NULLST);
- X else if ((c = skipws()) == ')')
- X break; /* Got them all */
- X else if (type[c] != LET) /* Bad formal syntax */
- X goto bad_define;
- X scanid(c); /* Get the formal param */
- X parlist[nargs++] = parmp; /* Save its start */
- X textput(tokenbuf); /* Save text in parm[] */
- X } while ((c = skipws()) == ','); /* Get another argument */
- X if (c != ')') /* Must end at ) */
- X goto bad_define;
- X c = ' '; /* Will skip to body */
- X }
- X else {
- X /*
- X * DEF_NOARGS is needed to distinguish between
- X * "#define foo" and "#define foo()".
- X */
- X nargs = DEF_NOARGS; /* No () parameters */
- X }
- X if (type[c] == SPA) /* At whitespace? */
- X c = skipws(); /* Not any more. */
- X workp = work; /* Replacement put here */
- X inmacro = TRUE; /* Keep \<newline> now */
- X quoting = 0; /* No # seen yet. */
- X while (c != EOF_CHAR && c != '\n') { /* Compile macro body */
- X#if OK_CONCAT
- X if (c == '#') { /* Token concatenation? */
- X if ((c = get()) != '#') { /* No, not really */
- X quoting = 1; /* Maybe quoting op. */
- X continue;
- X }
- X while (workp > work && type[workp[-1]] == SPA)
- X --workp; /* Erase leading spaces */
- X save(TOK_SEP); /* Stuff a delimiter */
- X c = skipws(); /* Eat whitespace */
- X#if 0
- X if (type[c] == LET) /* Another token here? */
- X ; /* Stuff it normally */
- X else if (type[c] == DIG) { /* Digit string after? */
- X while (type[c] == DIG) { /* Stuff the digits */
- X save(c); /* Note
- X c = get();
- X }
- X save(TOK_SEP); /* Delimit 2nd token */
- X }
- X else {
- X ciwarn("Strange character after ## (%d.)", c);
- X }
- X#endif
- X continue;
- X }
- X#endif
- X switch (type[c]) {
- X case LET:
- X#if OK_CONCAT
- X checkparm(c, dp, quoting); /* Might be a formal */
- X#else
- X checkparm(c, dp); /* Might be a formal */
- X#endif
- X break;
- X
- X case DIG: /* Number in mac. body */
- X case DOT: /* Maybe a float number */
- X scannumber(c, save); /* Scan it off */
- X break;
- X
- X case QUO: /* String in mac. body */
- X#if STRING_FORMAL
- X stparmscan(c, dp); /* Do string magic */
- X#else
- X stparmscan(c);
- X#endif
- X break;
- X
- X case BSH: /* Backslash */
- X save('\\');
- X if ((c = get()) == '\n')
- X wrongline = TRUE;
- X save(c);
- X break;
- X
- X case SPA: /* Absorb whitespace */
- X /*
- X * Note: the "end of comment" marker is passed on
- X * to allow comments to separate tokens.
- X */
- X if (workp[-1] == ' ') /* Absorb multiple */
- X break; /* spaces */
- X else if (c == '\t')
- X c = ' '; /* Normalize tabs */
- X /* Fall through to store character */
- X default: /* Other character */
- X save(c);
- X break;
- X }
- X c = get();
- X quoting = 0; /* Only when immediately*/
- X /* preceding a formal */
- X }
- X inmacro = FALSE; /* Stop newline hack */
- X unget(); /* For control check */
- X if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
- X workp--;
- X *workp = EOS; /* Terminate work */
- X dp->repl = savestring(work); /* Save the string */
- X dp->nargs = nargs; /* Save arg count */
- X#if DEBUG
- X if (debug)
- X dumpadef("macro definition", dp);
- X#endif
- X if (isredefine) { /* Error if redefined */
- X if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
- X || (old == NULL && dp->repl != NULL)
- X || (old != NULL && dp->repl == NULL)) {
- X cerror("Redefining defined variable \"%s\"", dp->name);
- X }
- X if (old != NULL) /* We don't need the */
- X free(old); /* old definition now. */
- X }
- X DBUG_VOID_RETURN;
- X
- Xbad_define:
- X cerror("#define syntax error", NULLST);
- X inmacro = FALSE; /* Stop <newline> hack */
- X DBUG_VOID_RETURN;
- X}
- X
- Xcheckparm(c, dp, quoting)
- Xregister int c;
- XDEFBUF *dp;
- Xint quoting; /* Preceded by a # ? */
- X/*
- X * Replace this param if it's defined. Note that the macro name is a
- X * possible replacement token. We stuff DEF_MAGIC in front of the token
- X * which is treated as a LETTER by the token scanner and eaten by
- X * the output routine. This prevents the macro expander from
- X * looping if someone writes "#define foo foo".
- X */
- X{
- X register int i;
- X register char *cp;
- X
- X DBUG_ENTER ("checkparm");
- X scanid(c); /* Get parm to tokenbuf */
- X for (i = 0; i < nargs; i++) { /* For each argument */
- X if (streq(parlist[i], tokenbuf)) { /* If it's known */
- X#if OK_CONCAT
- X if (quoting) /* Special handling of */
- X save(QUOTE_PARM); /* #formal inside defn */
- X#endif
- X save(i + MAC_PARM); /* Save a magic cookie */
- X DBUG_VOID_RETURN; /* And exit the search */
- X }
- X }
- X if (streq(dp->name, tokenbuf)) /* Macro name in body? */
- X save(DEF_MAGIC); /* Save magic marker */
- X for (cp = tokenbuf; *cp != EOS;) /* And save */
- X save(*cp++); /* The token itself */
- X DBUG_VOID_RETURN;
- X}
- X
- X#if STRING_FORMAL
- Xstparmscan(delim, dp)
- Xint delim;
- Xregister DEFBUF *dp;
- X/*
- X * Scan the string (starting with the given delimiter).
- X * The token is replaced if it is the only text in this string or
- X * character constant. The algorithm follows checkparm() above.
- X * Note that scanstring() has approved of the string.
- X */
- X{
- X register int c;
- X
- X DBUG_ENTER ("stparmscan");
- X /*
- X * Warning -- this code hasn't been tested for a while.
- X * It exists only to preserve compatibility with earlier
- X * implementations of cpp. It is not part of the Draft
- X * ANSI Standard C language.
- X */
- X save(delim);
- X instring = TRUE;
- X while ((c = get()) != delim
- X && c != '\n'
- X && c != EOF_CHAR) {
- X if (type[c] == LET) /* Maybe formal parm */
- X checkparm(c, dp, 0);
- X else {
- X save(c);
- X if (c == '\\')
- X save(get());
- X }
- X }
- X instring = FALSE;
- X if (c != delim)
- X cerror("Unterminated string in macro body", NULLST);
- X save(c);
- X DBUG_VOID_RETURN;
- X}
- X#else
- Xstparmscan(delim)
- Xint delim;
- X/*
- X * Normal string parameter scan.
- X */
- X{
- X register char *wp;
- X register int i;
- X extern int save();
- X
- X DBUG_ENTER ("stparmscan");
- X wp = workp; /* Here's where it starts */
- X if (!scanstring(delim, save))
- X DBUG_VOID_RETURN; /* Exit on scanstring error */
- X workp[-1] = EOS; /* Erase trailing quote */
- X wp++; /* -> first string content byte */
- X for (i = 0; i < nargs; i++) {
- X if (streq(parlist[i], wp)) {
- X *wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
- X *wp++ = (i + MAC_PARM); /* Make a formal marker */
- X *wp = wp[-3]; /* Add on closing quote */
- X workp = wp + 1; /* Reset string end */
- X DBUG_VOID_RETURN;
- X }
- X }
- X workp[-1] = wp[-1]; /* Nope, reset end quote. */
- X DBUG_VOID_RETURN;
- X}
- X#endif
- X
- Xdoundef()
- X/*
- X * Remove the symbol from the defined list.
- X * Called from the #control processor.
- X */
- X{
- X register int c;
- X
- X DBUG_ENTER ("doundef");
- X if (type[(c = skipws())] != LET)
- X cerror("Illegal #undef argument", NULLST);
- X else
- X {
- X scanid(c); /* Get name to tokenbuf */
- X (void) defendel(tokenbuf, TRUE);
- X }
- X DBUG_VOID_RETURN;
- X}
- X
- Xtextput(text)
- Xchar *text;
- X/*
- X * Put the string in the parm[] buffer.
- X */
- X{
- X register int size;
- X
- X DBUG_ENTER ("textput");
- X size = strlen(text) + 1;
- X if ((parmp + size) >= &parm[NPARMWORK])
- X cfatal("Macro work area overflow", NULLST);
- X else {
- X strcpy(parmp, text);
- X parmp += size;
- X }
- X DBUG_VOID_RETURN;
- X}
- X
- Xcharput(c)
- Xregister int c;
- X/*
- X * Put the byte in the parm[] buffer.
- X */
- X{
- X if (parmp >= &parm[NPARMWORK])
- X cfatal("Macro work area overflow", NULLST);
- X else {
- X *parmp++ = c;
- X }
- X}
- X
- X/*
- X * M a c r o E x p a n s i o n
- X */
- X
- Xstatic DEFBUF *macro; /* Catches start of infinite macro */
- X
- Xexpand(tokenp)
- Xregister DEFBUF *tokenp;
- X/*
- X * Expand a macro. Called from the cpp mainline routine (via subroutine
- X * macroid()) when a token is found in the symbol table. It calls
- X * expcollect() to parse actual parameters, checking for the correct number.
- X * It then creates a "file" containing a single line containing the
- X * macro with actual parameters inserted appropriately. This is
- X * "pushed back" onto the input stream. (When the get() routine runs
- X * off the end of the macro line, it will dismiss the macro itself.)
- X */
- X{
- X register int c;
- X register FILEINFO *file;
- X extern FILEINFO *getfile();
- X
- X DBUG_ENTER ("expand");
- X#if DEBUG
- X if (debug)
- X dumpadef("expand entry", tokenp);
- X#endif
- X /*
- X * If no macro is pending, save the name of this macro
- X * for an eventual error message.
- X */
- X if (recursion++ == 0)
- X macro = tokenp;
- X else if (recursion == RECURSION_LIMIT) {
- X cerror("Recursive macro definition of \"%s\"", tokenp->name);
- X fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
- X if (rec_recover) {
- X do {
- X c = get();
- X } while (infile != NULL && infile->fp == NULL);
- X unget();
- X recursion = 0;
- X DBUG_VOID_RETURN;
- X }
- X }
- X /*
- X * Here's a macro to expand.
- X */
- X nargs = 0; /* Formals counter */
- X parmp = parm; /* Setup parm buffer */
- X switch (tokenp->nargs) {
- X case (-2): /* __LINE__ */
- X sprintf(work, "%d", line);
- X ungetstring(work);
- X break;
- X
- X case (-3): /* __FILE__ */
- X for (file = infile; file != NULL; file = file->parent) {
- X if (file->fp != NULL) {
- X sprintf(work, "\"%s\"", (file->progname != NULL)
- X ? file->progname : file->filename);
- X ungetstring(work);
- X break;
- X }
- X }
- X break;
- X
- X default:
- X /*
- X * Nothing funny about this macro.
- X */
- X if (tokenp->nargs < 0)
- X cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
- X while ((c = skipws()) == '\n') /* Look for (, skipping */
- X wrongline = TRUE; /* spaces and newlines */
- X if (c != '(') {
- X /*
- X * If the programmer writes
- X * #define foo() ...
- X * ...
- X * foo [no ()]
- X * just write foo to the output stream.
- X */
- X unget();
- X cwarn("Macro \"%s\" needs arguments", tokenp->name);
- X fputs(tokenp->name, stdout);
- X DBUG_VOID_RETURN;
- X }
- X else if (expcollect()) { /* Collect arguments */
- X if (tokenp->nargs != nargs) { /* Should be an error? */
- X cwarn("Wrong number of macro arguments for \"%s\"",
- X tokenp->name);
- X }
- X#if DEBUG
- X if (debug)
- X dumpparm("expand");
- X#endif
- X } /* Collect arguments */
- X case DEF_NOARGS: /* No parameters just stuffs */
- X expstuff(tokenp); /* Do actual parameters */
- X } /* nargs switch */
- X DBUG_VOID_RETURN;
- X}
- X
- XFILE_LOCAL int
- Xexpcollect()
- X/*
- X * Collect the actual parameters for this macro. TRUE if ok.
- X */
- X{
- X register int c;
- X register int paren; /* For embedded ()'s */
- X extern int charput();
- X
- X DBUG_ENTER ("expcollect");
- X for (;;) {
- X paren = 0; /* Collect next arg. */
- X while ((c = skipws()) == '\n') /* Skip over whitespace */
- X wrongline = TRUE; /* and newlines. */
- X if (c == ')') { /* At end of all args? */
- X /*
- X * Note that there is a guard byte in parm[]
- X * so we don't have to check for overflow here.
- X */
- X *parmp = EOS; /* Make sure terminated */
- X break; /* Exit collection loop */
- X }
- X else if (nargs >= LASTPARM)
- X cfatal("Too many arguments in macro expansion", NULLST);
- X parlist[nargs++] = parmp; /* At start of new arg */
- X for (;; c = cget()) { /* Collect arg's bytes */
- X if (c == EOF_CHAR) {
- X cerror("end of file within macro argument", NULLST);
- X DBUG_RETURN (FALSE); /* Sorry. */
- X }
- X else if (c == '\\') { /* Quote next character */
- X charput(c); /* Save the \ for later */
- X charput(cget()); /* Save the next char. */
- X continue; /* And go get another */
- X }
- X else if (type[c] == QUO) { /* Start of string? */
- X scanstring(c, charput); /* Scan it off */
- X continue; /* Go get next char */
- X }
- X else if (c == '(') /* Worry about balance */
- X paren++; /* To know about commas */
- X else if (c == ')') { /* Other side too */
- X if (paren == 0) { /* At the end? */
- X unget(); /* Look at it later */
- X break; /* Exit arg getter. */
- X }
- X paren--; /* More to come. */
- X }
- X else if (c == ',' && paren == 0) /* Comma delimits args */
- X break;
- X else if (c == '\n') /* Newline inside arg? */
- X wrongline = TRUE; /* We'll need a #line */
- X charput(c); /* Store this one */
- X } /* Collect an argument */
- X charput(EOS); /* Terminate argument */
- X#if DEBUG
- X if (debug)
- X printf("parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
- X#endif
- X } /* Collect all args. */
- X DBUG_RETURN (TRUE); /* Normal return */
- X}
- X
- X
- X#if OK_CONCAT
- X
- XFILE_LOCAL
- Xchar *doquoting(to, from)
- Xregister char *to;
- Xregister char *from;
- X{
- X *to++ = '"';
- X while (*from) {
- X if (*from == '\\' || *from == '"')
- X *to++ = '\\';
- X *to++ = *from++;
- X }
- X *to++ = '"';
- X
- X return to;
- X}
- X
- X#endif
- X
- XFILE_LOCAL
- Xexpstuff(tokenp)
- XDEFBUF *tokenp; /* Current macro being expanded */
- X/*
- X * Stuff the macro body, replacing formal parameters by actual parameters.
- X */
- X{
- X register int c; /* Current character */
- X register char *inp; /* -> repl string */
- X register char *defp; /* -> macro output buff */
- X int size; /* Actual parm. size */
- X char *defend; /* -> output buff end */
- X int string_magic; /* String formal hack */
- X FILEINFO *file; /* Funny #include */
- X#if OK_CONCAT
- X register char quoting; /* Quote macro argument */
- X#endif
- X extern FILEINFO *getfile();
- X
- X DBUG_ENTER ("expstuff");
- X file = getfile(NBUFF, tokenp->name);
- X inp = tokenp->repl; /* -> macro replacement */
- X defp = file->buffer; /* -> output buffer */
- X defend = defp + (NBUFF - 1); /* Note its end */
- X if (inp != NULL) {
- X quoting = 0;
- X while ((c = (*inp++ & 0xFF)) != EOS) {
- X#if OK_CONCAT
- X if (c == QUOTE_PARM) { /* Special token for # */
- X quoting = 1; /* set flag, for later */
- X continue; /* Get next character */
- X }
- X#endif
- X if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) {
- X string_magic = (c == (MAC_PARM + PAR_MAC));
- X if (string_magic)
- X c = (*inp++ & 0xFF);
- X /*
- X * Replace formal parameter by actual parameter string.
- X */
- X if ((c -= MAC_PARM) < nargs) {
- X size = strlen(parlist[c]);
- X#if OK_CONCAT
- X if (quoting) {
- X size++;
- X size *= 2; /* worst case condition */
- X }
- X#endif
- X if ((defp + size) >= defend)
- X goto nospace;
- X /*
- X * Erase the extra set of quotes.
- X */
- X if (string_magic && defp[-1] == parlist[c][0]) {
- X strcpy(defp-1, parlist[c]);
- X defp += (size - 2);
- X }
- X#if OK_CONCAT
- X else if (quoting)
- X defp = doquoting(defp, parlist[c]);
- X#endif
- X else {
- X strcpy(defp, parlist[c]);
- X defp += size;
- X }
- X }
- X }
- X else if (defp >= defend) {
- Xnospace: cfatal("Out of space in macro \"%s\" arg expansion",
- X tokenp->name);
- X }
- X else {
- X *defp++ = c;
- X }
- X quoting = 0;
- X }
- X }
- X *defp = EOS;
- X#if DEBUG
- X if (debug > 1)
- X printf("macroline: \"%s\"\n", file->buffer);
- X#endif
- X DBUG_VOID_RETURN;
- X}
- X
- X#if DEBUG
- Xdumpparm(why)
- Xchar *why;
- X/*
- X * Dump parameter list.
- X */
- X{
- X register int i;
- X
- X DBUG_ENTER ("dumpparm");
- X printf("dump of %d parameters (%d bytes total) %s\n",
- X nargs, parmp - parm, why);
- X for (i = 0; i < nargs; i++) {
- X printf("parm[%d] (%d) = \"%s\"\n",
- X i + 1, strlen(parlist[i]), parlist[i]);
- X }
- X DBUG_VOID_RETURN;
- X}
- X#endif
- END_OF_FILE
- if test 19642 -ne `wc -c <'Cpp4.c'`; then
- echo shar: \"'Cpp4.c'\" unpacked with wrong size!
- fi
- # end of 'Cpp4.c'
- fi
- if test -f 'cpp5.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'cpp5.c'\"
- else
- echo shar: Extracting \"'cpp5.c'\" \(23086 characters\)
- sed "s/^X//" >'cpp5.c' <<'END_OF_FILE'
- X/*
- X * C P P 5 . C
- X * E x p r e s s i o n E v a l u a t i o n
- X *
- X * Edit History
- X * 31-Aug-84 MM USENET net.sources release
- X * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring()
- X * so they work correctly with token concatenation.
- X * Added string formal recognition.
- X * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we
- X * don't print unnecessary error messages for
- X * #if !defined(FOO) && FOO != 0 && 10 / FOO ...
- X * 31-Oct-84 ado/MM Added token concatenation
- X * 6-Nov-84 MM Split from #define stuff, added sizeof stuff
- X * 19-Nov-84 ado #if error returns TRUE for (sigh) compatibility
- X * 21-Oct-85 RMS Rename `token' to `tokenbuf'
- X * 23-Oct-85 RMS Treat undefined symbols as having value zero.
- X * 14-Mar-86 FNF Incorporate macro based C debugging package.
- X * Port to Commodore Amiga.
- X * 20-Aug-88 Ois Conditionally compile sizeof stuff.
- X */
- X
- X#include <stdio.h>
- X#include <ctype.h>
- X#include "cppdef.h"
- X#include "cpp.h"
- X
- X/*
- X * Evaluate an #if expression.
- X */
- X
- Xstatic char *opname[] = { /* For debug and error messages */
- X"end of expression", "val", "id",
- X "+", "-", "*", "/", "%",
- X "<<", ">>", "&", "|", "^",
- X "==", "!=", "<", "<=", ">=", ">",
- X "&&", "||", "?", ":", ",",
- X "unary +", "unary -", "~", "!", "(", ")", "(none)",
- X};
- X
- X/*
- X * opdope[] has the operator precedence:
- X * Bits
- X * 7 Unused (so the value is always positive)
- X * 6-2 Precedence (000x .. 017x)
- X * 1-0 Binary op. flags:
- X * 01 The binop flag should be set/cleared when this op is seen.
- X * 10 The new value of the binop flag.
- X * Note: Expected, New binop
- X * constant 0 1 Binop, end, or ) should follow constants
- X * End of line 1 0 End may not be preceeded by an operator
- X * binary 1 0 Binary op follows a value, value follows.
- X * unary 0 0 Unary op doesn't follow a value, value follows
- X * ( 0 0 Doesn't follow value, value or unop follows
- X * ) 1 1 Follows value. Op follows.
- X */
- X
- Xstatic char opdope[OP_MAX] = {
- X 0001, /* End of expression */
- X 0002, /* Digit */
- X 0000, /* Letter (identifier) */
- X 0141, 0141, 0151, 0151, 0151, /* ADD, SUB, MUL, DIV, MOD */
- X 0131, 0131, 0101, 0071, 0071, /* ASL, ASR, AND, OR, XOR */
- X 0111, 0111, 0121, 0121, 0121, 0121, /* EQ, NE, LT, LE, GE, GT */
- X 0061, 0051, 0041, 0041, 0031, /* ANA, ORO, QUE, COL, CMA */
- X/*
- X * Unary op's follow
- X */
- X 0160, 0160, 0160, 0160, /* NEG, PLU, COM, NOT */
- X 0170, 0013, 0023, /* LPA, RPA, END */
- X};
- X/*
- X * OP_QUE and OP_RPA have alternate precedences:
- X */
- X#define OP_RPA_PREC 0013
- X#define OP_QUE_PREC 0034
- X
- X/*
- X * S_ANDOR and S_QUEST signal "short-circuit" boolean evaluation, so that
- X * #if FOO != 0 && 10 / FOO ...
- X * doesn't generate an error message. They are stored in optab.skip.
- X */
- X#define S_ANDOR 2
- X#define S_QUEST 1
- X
- Xtypedef struct optab {
- X char op; /* Operator */
- X char prec; /* Its precedence */
- X char skip; /* Short-circuit: TRUE to skip */
- X} OPTAB;
- Xstatic int evalue; /* Current value from evallex() */
- X
- X#ifdef nomacargs
- XFILE_LOCAL int
- Xisbinary(op)
- Xregister int op;
- X{
- X return (op >= FIRST_BINOP && op <= LAST_BINOP);
- X}
- X
- XFILE_LOCAL int
- Xisunary(op)
- Xregister int op;
- X{
- X return (op >= FIRST_UNOP && op <= LAST_UNOP);
- X}
- X#else
- X#define isbinary(op) (op >= FIRST_BINOP && op <= LAST_BINOP)
- X#define isunary(op) (op >= FIRST_UNOP && op <= LAST_UNOP)
- X#endif
- X
- X/*
- X * The following definitions are used to specify basic variable sizes.
- X */
- X
- X#if OK_SIZEOF
- X
- X#ifndef S_CHAR
- X#define S_CHAR (sizeof (char))
- X#endif
- X#ifndef S_SINT
- X#ifdef manx /* Aztec/Manx C does not like "short int" */
- X#define S_SINT (sizeof (short))
- X#else
- X#define S_SINT (sizeof (short int))
- X#endif
- X#endif
- X#ifndef S_INT
- X#define S_INT (sizeof (int))
- X#endif
- X#ifndef S_LINT
- X#define S_LINT (sizeof (long int))
- X#endif
- X#ifndef S_FLOAT
- X#define S_FLOAT (sizeof (float))
- X#endif
- X#ifndef S_DOUBLE
- X#define S_DOUBLE (sizeof (double))
- X#endif
- X#ifndef S_PCHAR
- X#define S_PCHAR (sizeof (char *))
- X#endif
- X#ifndef S_PSINT
- X#ifdef manx /* Aztec/Manx C does not like "short int" */
- X#define S_PSINT (sizeof (short *))
- X#else
- X#define S_PSINT (sizeof (short int *))
- X#endif
- X#endif
- X#ifndef S_PINT
- X#define S_PINT (sizeof (int *))
- X#endif
- X#ifndef S_PLINT
- X#define S_PLINT (sizeof (long int *))
- X#endif
- X#ifndef S_PFLOAT
- X#define S_PFLOAT (sizeof (float *))
- X#endif
- X#ifndef S_PDOUBLE
- X#define S_PDOUBLE (sizeof (double *))
- X#endif
- X#ifndef S_PFPTR
- X#define S_PFPTR (sizeof (int (*)()))
- X#endif
- X
- X
- Xtypedef struct types {
- X short type; /* This is the bit if */
- X char *name; /* this is the token word */
- X} TYPES;
- X
- Xstatic TYPES basic_types[] = {
- X { T_CHAR, "char", },
- X { T_INT, "int", },
- X { T_FLOAT, "float", },
- X { T_DOUBLE, "double", },
- X { T_SHORT, "short", },
- X { T_LONG, "long", },
- X { T_SIGNED, "signed", },
- X { T_UNSIGNED, "unsigned", },
- X { 0, NULL, }, /* Signal end */
- X};
- X
- X/*
- X * Test_table[] is used to test for illegal combinations.
- X */
- Xstatic short test_table[] = {
- X T_FLOAT | T_DOUBLE | T_LONG | T_SHORT,
- X T_FLOAT | T_DOUBLE | T_CHAR | T_INT,
- X T_FLOAT | T_DOUBLE | T_SIGNED | T_UNSIGNED,
- X T_LONG | T_SHORT | T_CHAR,
- X 0 /* end marker */
- X};
- X
- X/*
- X * The order of this table is important -- it is also referenced by
- X * the command line processor to allow run-time overriding of the
- X * built-in size values. The order must not be changed:
- X * char, short, int, long, float, double (func pointer)
- X */
- XSIZES size_table[] = {
- X { T_CHAR, S_CHAR, S_PCHAR }, /* char */
- X { T_SHORT, S_SINT, S_PSINT }, /* short int */
- X { T_INT, S_INT, S_PINT }, /* int */
- X { T_LONG, S_LINT, S_PLINT }, /* long */
- X { T_FLOAT, S_FLOAT, S_PFLOAT }, /* float */
- X { T_DOUBLE, S_DOUBLE, S_PDOUBLE }, /* double */
- X { T_FPTR, 0, S_PFPTR }, /* int (*()) */
- X { 0, 0, 0 }, /* End of table */
- X};
- X
- X#endif /* OK_SIZEOF */
- X
- Xint
- Xeval()
- X/*
- X * Evaluate an expression. Straight-forward operator precedence.
- X * This is called from control() on encountering an #if statement.
- X * It calls the following routines:
- X * evallex Lexical analyser -- returns the type and value of
- X * the next input token.
- X * evaleval Evaluate the current operator, given the values on
- X * the value stack. Returns a pointer to the (new)
- X * value stack.
- X * For compatiblity with older cpp's, this return returns 1 (TRUE)
- X * if a syntax error is detected.
- X */
- X{
- X register int op; /* Current operator */
- X register int *valp; /* -> value vector */
- X register OPTAB *opp; /* Operator stack */
- X int prec; /* Op precedence */
- X int binop; /* Set if binary op. needed */
- X int op1; /* Operand from stack */
- X int skip; /* For short-circuit testing */
- X int value[NEXP]; /* Value stack */
- X OPTAB opstack[NEXP]; /* Operand stack */
- X extern int *evaleval(); /* Does actual evaluation */
- X
- X DBUG_ENTER ("eval");
- X valp = value;
- X opp = opstack;
- X opp->op = OP_END; /* Mark bottom of stack */
- X opp->prec = opdope[OP_END]; /* And its precedence */
- X opp->skip = 0; /* Not skipping now */
- X binop = 0;
- Xagain: ;
- X#ifdef DEBUG_EVAL
- X printf("In #if at again: skip = %d, binop = %d, line is: %s",
- X opp->skip, binop, infile->bptr);
- X#endif
- X if ((op = evallex(opp->skip)) == OP_SUB && binop == 0)
- X op = OP_NEG; /* Unary minus */
- X else if (op == OP_ADD && binop == 0)
- X op = OP_PLU; /* Unary plus */
- X else if (op == OP_FAIL)
- X DBUG_RETURN (1); /* Error in evallex */
- X#ifdef DEBUG_EVAL
- X printf("op = %s, opdope = %03o, binop = %d, skip = %d\n",
- X opname[op], opdope[op], binop, opp->skip);
- X#endif
- X if (op == DIG) { /* Value? */
- X if (binop != 0) {
- X cerror("misplaced constant in #if", NULLST);
- X DBUG_RETURN (1);
- X }
- X else if (valp >= &value[NEXP-1]) {
- X cerror("#if value stack overflow", NULLST);
- X DBUG_RETURN (1);
- X }
- X else {
- X#ifdef DEBUG_EVAL
- X printf("pushing %d onto value stack[%d]\n",
- X evalue, valp - value);
- X#endif
- X *valp++ = evalue;
- X binop = 1;
- X }
- X goto again;
- X }
- X else if (op > OP_END) {
- X cerror("Illegal #if line", NULLST);
- X DBUG_RETURN (1);
- X }
- X prec = opdope[op];
- X if (binop != (prec & 1)) {
- X cerror("Operator %s in incorrect context", opname[op]);
- X DBUG_RETURN (1);
- X }
- X binop = (prec & 2) >> 1;
- X for (;;) {
- X#ifdef DEBUG_EVAL
- X printf("op %s, prec %d., stacked op %s, prec %d, skip %d\n",
- X opname[op], prec, opname[opp->op], opp->prec, opp->skip);
- X#endif
- X if (prec > opp->prec) {
- X if (op == OP_LPA)
- X prec = OP_RPA_PREC;
- X else if (op == OP_QUE)
- X prec = OP_QUE_PREC;
- X op1 = opp->skip; /* Save skip for test */
- X /*
- X * Push operator onto op. stack.
- X */
- X opp++;
- X if (opp >= &opstack[NEXP]) {
- X cerror("expression stack overflow at op \"%s\"",
- X opname[op]);
- X DBUG_RETURN (1);
- X }
- X opp->op = op;
- X opp->prec = prec;
- X skip = (valp[-1] != 0); /* Short-circuit tester */
- X /*
- X * Do the short-circuit stuff here. Short-circuiting
- X * stops automagically when operators are evaluated.
- X */
- X if ((op == OP_ANA && !skip)
- X || (op == OP_ORO && skip))
- X opp->skip = S_ANDOR; /* And/or skip starts */
- X else if (op == OP_QUE) /* Start of ?: operator */
- X opp->skip = (op1 & S_ANDOR) | ((!skip) ? S_QUEST : 0);
- X else if (op == OP_COL) { /* : inverts S_QUEST */
- X opp->skip = (op1 & S_ANDOR)
- X | (((op1 & S_QUEST) != 0) ? 0 : S_QUEST);
- X }
- X else { /* Other ops leave */
- X opp->skip = op1; /* skipping unchanged. */
- X }
- X#ifdef DEBUG_EVAL
- X printf("stacking %s, valp[-1] == %d at %s",
- X opname[op], valp[-1], infile->bptr);
- X dumpstack(opstack, opp, value, valp);
- X#endif
- X goto again;
- X }
- X /*
- X * Pop operator from op. stack and evaluate it.
- X * End of stack and '(' are specials.
- X */
- X skip = opp->skip; /* Remember skip value */
- X switch ((op1 = opp->op)) { /* Look at stacked op */
- X case OP_END: /* Stack end marker */
- X if (op == OP_EOE)
- X DBUG_RETURN (valp[-1]); /* Finished ok. */
- X goto again; /* Read another op. */
- X
- X case OP_LPA: /* ( on stack */
- X if (op != OP_RPA) { /* Matches ) on input */
- X cerror("unbalanced paren's, op is \"%s\"", opname[op]);
- X DBUG_RETURN (1);
- X }
- X opp--; /* Unstack it */
- X /* goto again; -- Fall through */
- X
- X case OP_QUE:
- X goto again; /* Evaluate true expr. */
- X
- X case OP_COL: /* : on stack. */
- X opp--; /* Unstack : */
- X if (opp->op != OP_QUE) { /* Matches ? on stack? */
- X cerror("Misplaced '?' or ':', previous operator is %s",
- X opname[opp->op]);
- X DBUG_RETURN (1);
- X }
- X /*
- X * Evaluate op1.
- X */
- X default: /* Others: */
- X opp--; /* Unstack the operator */
- X#ifdef DEBUG_EVAL
- X printf("Stack before evaluation of %s\n", opname[op1]);
- X dumpstack(opstack, opp, value, valp);
- X#endif
- X valp = evaleval(valp, op1, skip);
- X#ifdef DEBUG_EVAL
- X printf("Stack after evaluation\n");
- X dumpstack(opstack, opp, value, valp);
- X#endif
- X } /* op1 switch end */
- X } /* Stack unwind loop */
- X}
- X
- XFILE_LOCAL int
- Xevallex(skip)
- Xint skip; /* TRUE if short-circuit evaluation */
- X/*
- X * Return next eval operator or value. Called from eval(). It
- X * calls a special-purpose routines for 'char' strings and
- X * numeric values:
- X * evalchar called to evaluate 'x'
- X * evalnum called to evaluate numbers.
- X */
- X{
- X register int c, c1, t;
- X
- X DBUG_ENTER ("evallex");
- Xagain: do { /* Collect the token */
- X c = skipws();
- X if ((c = macroid(c)) == EOF_CHAR || c == '\n') {
- X unget();
- X DBUG_RETURN (OP_EOE); /* End of expression */
- X }
- X } while ((t = type[c]) == LET && catenate());
- X if (t == INV) { /* Total nonsense */
- X if (!skip) {
- X if (isascii(c) && isprint(c))
- X cierror("illegal character '%c' in #if", c);
- X else
- X cierror("illegal character (%d decimal) in #if", c);
- X }
- X DBUG_RETURN (OP_FAIL);
- X }
- X else if (t == QUO) { /* ' or " */
- X if (c == '\'') { /* Character constant */
- X evalue = evalchar(skip); /* Somewhat messy */
- X#ifdef DEBUG_EVAL
- X printf("evalchar returns %d.\n", evalue);
- X#endif
- X DBUG_RETURN (DIG); /* Return a value */
- X }
- X cerror("Can't use a string in an #if", NULLST);
- X DBUG_RETURN (OP_FAIL);
- X }
- X else if (t == LET) { /* ID must be a macro */
- X if (streq(tokenbuf, "defined")) { /* Or defined name */
- X c1 = c = skipws();
- X if (c == '(') /* Allow defined(name) */
- X c = skipws();
- X if (type[c] == LET) {
- X evalue = (lookid(c) != NULL);
- X if (c1 != '(' /* Need to balance */
- X || skipws() == ')') /* Did we balance? */
- X DBUG_RETURN (DIG); /* Parsed ok */
- X }
- X cerror("Bad #if ... defined() syntax", NULLST);
- X DBUG_RETURN (OP_FAIL);
- X }
- X#if OK_SIZEOF
- X else if (streq(tokenbuf, "sizeof")) /* New sizeof hackery */
- X DBUG_RETURN (dosizeof()); /* Gets own routine */
- X#endif
- X evalue = 0;
- X DBUG_RETURN (DIG);
- X }
- X else if (t == DIG) { /* Numbers are harder */
- X evalue = evalnum(c);
- X#ifdef DEBUG_EVAL
- X printf("evalnum returns %d.\n", evalue);
- X#endif
- X }
- X else if (strchr("!=<>&|\\", c) != NULL) {
- X /*
- X * Process a possible multi-byte lexeme.
- X */
- X c1 = cget(); /* Peek at next char */
- X switch (c) {
- X case '!':
- X if (c1 == '=')
- X DBUG_RETURN (OP_NE);
- X break;
- X
- X case '=':
- X if (c1 != '=') { /* Can't say a=b in #if */
- X unget();
- X cerror("= not allowed in #if", NULLST);
- X DBUG_RETURN (OP_FAIL);
- X }
- X DBUG_RETURN (OP_EQ);
- X
- X case '>':
- X case '<':
- X if (c1 == c)
- X DBUG_RETURN ((c == '<') ? OP_ASL : OP_ASR);
- X else if (c1 == '=')
- X DBUG_RETURN ((c == '<') ? OP_LE : OP_GE);
- X break;
- X
- X case '|':
- X case '&':
- X if (c1 == c)
- X DBUG_RETURN ((c == '|') ? OP_ORO : OP_ANA);
- X break;
- X
- X case '\\':
- X if (c1 == '\n') /* Multi-line if */
- X goto again;
- X cerror("Unexpected \\ in #if", NULLST);
- X DBUG_RETURN (OP_FAIL);
- X }
- X unget();
- X }
- X DBUG_RETURN (t);
- X}
- X
- X#if OK_SIZEOF
- X
- XFILE_LOCAL int
- Xdosizeof()
- X/*
- X * Process the sizeof (basic type) operation in an #if string.
- X * Sets evalue to the size and returns
- X * DIG success
- X * OP_FAIL bad parse or something.
- X */
- X{
- X register int c;
- X register TYPES *tp;
- X register SIZES *sizp;
- X register short *testp;
- X short typecode;
- X
- X DBUG_ENTER ("dosizeof");
- X if ((c = skipws()) != '(')
- X goto nogood;
- X /*
- X * Scan off the tokens.
- X */
- X typecode = 0;
- X while ((c = skipws())) {
- X if ((c = macroid(c)) == EOF_CHAR || c == '\n')
- X goto nogood; /* End of line is a bug */
- X else if (c == '(') { /* thing (*)() func ptr */
- X if (skipws() == '*'
- X && skipws() == ')') { /* We found (*) */
- X if (skipws() != '(') /* Let () be optional */
- X unget();
- X else if (skipws() != ')')
- X goto nogood;
- X typecode |= T_FPTR; /* Function pointer */
- X }
- X else { /* Junk is a bug */
- X goto nogood;
- X }
- X }
- X else if (type[c] != LET) /* Exit if not a type */
- X break;
- X else if (!catenate()) { /* Maybe combine tokens */
- X /*
- X * Look for this unexpandable token in basic_types.
- X * The code accepts "int long" as well as "long int"
- X * which is a minor bug as bugs go (and one shared with
- X * a lot of C compilers).
- X */
- X for (tp = basic_types; tp->name != NULLST; tp++) {
- X if (streq(tokenbuf, tp->name))
- X break;
- X }
- X if (tp->name == NULLST) {
- X cerror("#if sizeof, unknown type \"%s\"", tokenbuf);
- X DBUG_RETURN (OP_FAIL);
- X }
- X typecode |= tp->type; /* Or in the type bit */
- X }
- X }
- X /*
- X * We are at the end of the type scan. Chew off '*' if necessary.
- X */
- X if (c == '*') {
- X typecode |= T_PTR;
- X c = skipws();
- X }
- X if (c == ')') { /* Last syntax check */
- X for (testp = test_table; *testp != 0; testp++) {
- X if (!bittest(typecode & *testp)) {
- X cerror("#if ... sizeof: illegal type combination", NULLST);
- X DBUG_RETURN (OP_FAIL);
- X }
- X }
- X /*
- X * We assume that all function pointers are the same size:
- X * sizeof (int (*)()) == sizeof (float (*)())
- X * We assume that signed and unsigned don't change the size:
- X * sizeof (signed int) == (sizeof unsigned int)
- X */
- X if ((typecode & T_FPTR) != 0) /* Function pointer */
- X typecode = T_FPTR | T_PTR;
- X else { /* Var or var * datum */
- X typecode &= ~(T_SIGNED | T_UNSIGNED);
- X if ((typecode & (T_SHORT | T_LONG)) != 0)
- X typecode &= ~T_INT;
- X }
- X if ((typecode & ~T_PTR) == 0) {
- X cerror("#if sizeof() error, no type specified", NULLST);
- X DBUG_RETURN (OP_FAIL);
- X }
- X /*
- X * Exactly one bit (and possibly T_PTR) may be set.
- X */
- X for (sizp = size_table; sizp->bits != 0; sizp++) {
- X if ((typecode & ~T_PTR) == sizp->bits) {
- X evalue = ((typecode & T_PTR) != 0)
- X ? sizp->psize : sizp->size;
- X DBUG_RETURN (DIG);
- X }
- X } /* We shouldn't fail */
- X cierror("#if ... sizeof: bug, unknown type code 0x%x", typecode);
- X DBUG_RETURN (OP_FAIL);
- X }
- X
- Xnogood: unget();
- X cerror("#if ... sizeof() syntax error", NULLST);
- X DBUG_RETURN (OP_FAIL);
- X}
- X
- XFILE_LOCAL int
- Xbittest(value)
- X/*
- X * TRUE if value is zero or exactly one bit is set in value.
- X */
- X{
- X#if (4096 & ~(-4096)) == 0
- X return ((value & ~(-value)) == 0);
- X#else
- X /*
- X * Do it the hard way (for non 2's complement machines)
- X */
- X return (value == 0 || value ^ (value - 1) == (value * 2 - 1));
- X#endif
- X}
- X
- X#endif /* OK_SIZEOF */
- X
- XFILE_LOCAL int
- Xevalnum(c)
- Xregister int c;
- X/*
- X * Expand number for #if lexical analysis. Note: evalnum recognizes
- X * the unsigned suffix, but only returns a signed int value.
- X */
- X{
- X register int value;
- X register int base;
- X register int c1;
- X
- X DBUG_ENTER ("evalnum");
- X if (c != '0')
- X base = 10;
- X else if ((c = cget()) == 'x' || c == 'X') {
- X base = 16;
- X c = cget();
- X }
- X else base = 8;
- X value = 0;
- X for (;;) {
- X c1 = c;
- X if (isascii(c) && isupper(c1))
- X c1 = tolower(c1);
- X if (c1 >= 'a')
- X c1 -= ('a' - 10);
- X else c1 -= '0';
- X if (c1 < 0 || c1 >= base)
- X break;
- X value *= base;
- X value += c1;
- X c = cget();
- X }
- X if (c == 'u' || c == 'U') /* Unsigned nonsense */
- X c = cget();
- X unget();
- X DBUG_RETURN (value);
- X}
- X
- XFILE_LOCAL int
- Xevalchar(skip)
- Xint skip; /* TRUE if short-circuit evaluation */
- X/*
- X * Get a character constant
- X */
- X{
- X register int c;
- X register int value;
- X register int count;
- X
- X DBUG_ENTER ("evalchar");
- X instring = TRUE;
- X if ((c = cget()) == '\\') {
- X switch ((c = cget())) {
- X case 'a': /* New in Standard */
- X#if ('a' == '\a' || '\a' == ALERT)
- X value = ALERT; /* Use predefined value */
- X#else
- X value = '\a'; /* Use compiler's value */
- X#endif
- X break;
- X
- X case 'b':
- X value = '\b';
- X break;
- X
- X case 'f':
- X value = '\f';
- X break;
- X
- X case 'n':
- X value = '\n';
- X break;
- X
- X case 'r':
- X value = '\r';
- X break;
- X
- X case 't':
- X value = '\t';
- X break;
- X
- X case 'v': /* New in Standard */
- X#if ('v' == '\v' || '\v' == VT)
- X value = VT; /* Use predefined value */
- X#else
- X value = '\v'; /* Use compiler's value */
- X#endif
- X break;
- X
- X case 'x': /* '\xFF' */
- X count = 3;
- X value = 0;
- X while ((((c = get()) >= '0' && c <= '9')
- X || (c >= 'a' && c <= 'f')
- X || (c >= 'A' && c <= 'F'))
- X && (--count >= 0)) {
- X value *= 16;
- X value += (c <= '9') ? (c - '0') : ((c & 0xF) + 9);
- X }
- X unget();
- X break;
- X
- X default:
- X if (c >= '0' && c <= '7') {
- X count = 3;
- X value = 0;
- X while (c >= '0' && c <= '7' && --count >= 0) {
- X value *= 8;
- X value += (c - '0');
- X c = get();
- X }
- X unget();
- X }
- X else value = c;
- X break;
- X }
- X }
- X else if (c == '\'')
- X value = 0;
- X else value = c;
- X /*
- X * We warn on multi-byte constants and try to hack
- X * (big|little)endian machines.
- X */
- X#if BIG_ENDIAN
- X count = 0;
- X#endif
- X while ((c = get()) != '\'' && c != EOF_CHAR && c != '\n') {
- X if (!skip)
- X ciwarn("multi-byte constant '%c' isn't portable", c);
- X#if BIG_ENDIAN
- X count += BITS_CHAR;
- X value += (c << count);
- X#else
- X value <<= BITS_CHAR;
- X value += c;
- X#endif
- X }
- X instring = FALSE;
- X DBUG_RETURN (value);
- X}
- X
- XFILE_LOCAL int *
- Xevaleval(valp, op, skip)
- Xregister int *valp;
- Xint op;
- Xint skip; /* TRUE if short-circuit evaluation */
- X/*
- X * Apply the argument operator to the data on the value stack.
- X * One or two values are popped from the value stack and the result
- X * is pushed onto the value stack.
- X *
- X * OP_COL is a special case.
- X *
- X * evaleval() returns the new pointer to the top of the value stack.
- X */
- X{
- X register int v1, v2;
- X
- X DBUG_ENTER ("evaleval");
- X if (isbinary(op))
- X v2 = *--valp;
- X v1 = *--valp;
- X#ifdef DEBUG_EVAL
- X printf("%s op %s", (isbinary(op)) ? "binary" : "unary",
- X opname[op]);
- X if (isbinary(op))
- X printf(", v2 = %d.", v2);
- X printf(", v1 = %d.\n", v1);
- X#endif
- X switch (op) {
- X case OP_EOE:
- X break;
- X
- X case OP_ADD:
- X v1 += v2;
- X break;
- X
- X case OP_SUB:
- X v1 -= v2;
- X break;
- X
- X case OP_MUL:
- X v1 *= v2;
- X break;
- X
- X case OP_DIV:
- X case OP_MOD:
- X if (v2 == 0) {
- X if (!skip) {
- X cwarn("%s by zero in #if, zero result assumed",
- X (op == OP_DIV) ? "divide" : "mod");
- X }
- X v1 = 0;
- X }
- X else if (op == OP_DIV)
- X v1 /= v2;
- X else
- X v1 %= v2;
- X break;
- X
- X case OP_ASL:
- X v1 <<= v2;
- X break;
- X
- X case OP_ASR:
- X v1 >>= v2;
- X break;
- X
- X case OP_AND:
- X v1 &= v2;
- X break;
- X
- X case OP_OR:
- X v1 |= v2;
- X break;
- X
- X case OP_XOR:
- X v1 ^= v2;
- X break;
- X
- X case OP_EQ:
- X v1 = (v1 == v2);
- X break;
- X
- X case OP_NE:
- X v1 = (v1 != v2);
- X break;
- X
- X case OP_LT:
- X v1 = (v1 < v2);
- X break;
- X
- X case OP_LE:
- X v1 = (v1 <= v2);
- X break;
- X
- X case OP_GE:
- X v1 = (v1 >= v2);
- X break;
- X
- X case OP_GT:
- X v1 = (v1 > v2);
- X break;
- X
- X case OP_ANA:
- X v1 = (v1 && v2);
- X break;
- X
- X case OP_ORO:
- X v1 = (v1 || v2);
- X break;
- X
- X case OP_COL:
- X /*
- X * v1 has the "true" value, v2 the "false" value.
- X * The top of the value stack has the test.
- X */
- X v1 = (*--valp) ? v1 : v2;
- X break;
- X
- X case OP_NEG:
- X v1 = (-v1);
- X break;
- X
- X case OP_PLU:
- X break;
- X
- X case OP_COM:
- X v1 = ~v1;
- X break;
- X
- X case OP_NOT:
- X v1 = !v1;
- X break;
- X
- X default:
- X cierror("#if bug, operand = %d.", op);
- X v1 = 0;
- X }
- X *valp++ = v1;
- X DBUG_RETURN (valp);
- X}
- X
- X#ifdef DEBUG_EVAL
- Xdumpstack(opstack, opp, value, valp)
- XOPTAB opstack[NEXP]; /* Operand stack */
- Xregister OPTAB *opp; /* Operator stack */
- Xint value[NEXP]; /* Value stack */
- Xregister int *valp; /* -> value vector */
- X{
- X DBUG_ENTER ("dumpstack");
- X printf("index op prec skip name -- op stack at %s", infile->bptr);
- X while (opp > opstack) {
- X printf(" [%2d] %2d %03o %d %s\n", opp - opstack,
- X opp->op, opp->prec, opp->skip, opname[opp->op]);
- X opp--;
- X }
- X while (--valp >= value) {
- X printf("value[%d] = %d\n", (valp - value), *valp);
- X }
- X DBUG_VOID_RETURN;
- X}
- X#endif
- X
- END_OF_FILE
- if test 23086 -ne `wc -c <'cpp5.c'`; then
- echo shar: \"'cpp5.c'\" unpacked with wrong size!
- fi
- # end of 'cpp5.c'
- fi
- echo shar: End of archive 3 \(of 5\).
- cp /dev/null ark3isdone
- MISSING=""
- for I in 1 2 3 4 5 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 5 archives.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
- --
- Submissions to comp.sources.amiga and comp.binaries.amiga should be sent to:
- amiga@cs.odu.edu
- or amiga@xanth.cs.odu.edu ( obsolescent mailers may need this address )
- or ...!uunet!xanth!amiga ( very obsolescent mailers need this address )
-
- Comments, questions, and suggestions s should be addressed to ``amiga-request''
- (only use ``amiga'' for submissions) at the above addresses.
-